import math

from PyQt5 import QtGui
from PyQt5.QtGui import QColor, QPen
from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsRectItem, QApplication, QGraphicsTextItem
from PyQt5.QtCore import Qt, QRectF, QPointF, QSizeF
import sys
from loguru import logger


class ResizableRect(QGraphicsRectItem):
    def __init__(self,
                 rect,
                 max_resize_x,
                 max_resize_y,
                 classes_name='0',
                 mouse_release_event_hook=None,
                 mouse_move_event_hook=None,
                 w_scale=1,
                 h_scale=1,
                 pic_path=None,
                 no_hover_pen_color: QColor = QColor(0, 0, 255, 140)):
        """
        可调整大小的矩形
        :param rect:  QRectF对象，矩形的位置和大小
        :param max_resize_x:  矩形可拖拽到的最大右侧位置
        :param max_resize_y:  矩形可拖拽到的最大底部位置
        :param classes_name:  类别名称
        :param mouse_release_event_hook:  鼠标释放事件的钩子函数
        :param mouse_move_event_hook:  鼠标移动过程中事件的钩子函数
        :param w_scale:  宽度比例尺
        :param h_scale:  高度比例尺
        :param pic_path:  图片路径
        :param no_hover_pen_color:  未选中时的边框颜色
        """
        super().__init__(rect)
        self.setFlag(QGraphicsRectItem.ItemIsSelectable, False)
        self.setAcceptHoverEvents(True)
        self.line_width = 6
        # 未选中设置边框属性，蓝色 透明度为140，宽度为 6 的线
        # self.no_hover_pen = QPen(QColor(0, 0, 255, 140), self.line_width)
        self.no_hover_pen = QPen(no_hover_pen_color, self.line_width)
        # 拷贝一份no_hover_pen，然后设置透明色为255
        cp_pen_color = QColor(no_hover_pen_color)
        cp_pen_color.setAlpha(255)
        self.hover_pen = QPen(cp_pen_color, self.line_width)
        self.setPen(self.no_hover_pen)
        # 去除选中时的虚线框
        self.dragging = False
        self.resizing = False
        # 手柄的大小，即多大的区域会触发缩放鼠标样式
        self.resize_handle_size = 15
        # 记录当前调整的方向
        self.resize_direction = None
        # 记录上一个鼠标位置
        self.last_pos = None
        # 设置矩形的最小大小
        self.min_size = 40
        self.max_resize_x = max_resize_x
        self.max_resize_y = max_resize_y
        # 类别名称
        self.classes_name = classes_name
        # 鼠标释放事件的钩子函数
        self.mouse_release_event_hook = mouse_release_event_hook
        # 鼠标移动事件的钩子函数
        self.mouse_move_event_hook = mouse_move_event_hook
        # 设置比例尺
        self.w_scale = w_scale
        self.h_scale = h_scale
        # 设置图片路径
        self.pic_path = pic_path
        # 创建文本标签
        self.text_item = QGraphicsTextItem('0x0 0', self)
        # 将文本居中放置在矩形中
        self.update_label_position()
        # 更新文本标签的内容
        self.update_label_text()

    def hoverMoveEvent(self, event):
        if not self.is_mouse_over_resize_handle(event):
            self.setCursor(Qt.ArrowCursor)
        super().hoverMoveEvent(event)

    def mousePressEvent(self, event):
        # 设置当前框的边框色alpha为满
        self.setPen(self.hover_pen)
        # 设置当前的index为最高
        self.setZValue(1)
        # 设置其他的框的边框色alpha为140
        for item in self.scene().items():
            if isinstance(item, ResizableRect) and item != self:
                item.setPen(item.no_hover_pen)
                item.setZValue(0)
        if self.is_mouse_over_resize_handle(event):
            self.resizing = True
            self.resize_direction = self.get_resize_direction(event)
        else:
            self.dragging = True
            self.last_pos = event.scenePos()  # 记录点击位置
            self.setCursor(Qt.ClosedHandCursor)
        if self.mouse_move_event_hook:
            self.show_small_pic()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        if self.resizing:
            self.resize_rect(event)
            # 更新文本标签的信息
            self.update_label_text()
            self.update_label_position()
        elif self.dragging:
            if self.last_pos is not None:
                # 计算鼠标的移动距离
                delta = event.scenePos() - self.last_pos
                # 更新矩形的位置
                new_pos = self.pos() + delta
                # 限制矩形的移动范围为 新的位置为new_pos.x()+self.rect().topLeft() > 0 and new_pos.y()+self.rect().height() > 0
                if (new_pos.x() + self.rect().topLeft().x() - self.line_width > 0
                        and new_pos.y() + self.rect().topLeft().y() - self.line_width > 0
                        and new_pos.x() + self.rect().topLeft().x() + self.rect().width() + self.line_width < self.max_resize_x
                        and new_pos.y() + self.rect().topLeft().y() + self.rect().height() + self.line_width < self.max_resize_y):
                    # 设置新的矩形位置
                    self.setPos(new_pos.x(), new_pos.y())
                self.last_pos = event.scenePos()  # 更新上一个位置
        if self.mouse_move_event_hook:
            self.show_small_pic()
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        self.dragging = False
        self.resizing = False
        # 重置上一个位置
        self.last_pos = None
        logger.info('release')
        if self.mouse_release_event_hook:
            QApplication.processEvents()
            self.mouse_release_event_hook(self.w_scale, self.h_scale, self.pic_path)
        if self.mouse_move_event_hook:
            self.show_small_pic()
        super().mouseReleaseEvent(event)

    def get_resize_direction(self, event):
        # 取四个角的区域，以self.rect()的四个角为中心，宽高为self.resize_handle_size*2
        handles = self.get_handles()
        for index, handle in enumerate(handles):
            # 左上角,右上角,左下角,右下角
            if handle.contains(event.pos()):
                return ['top-left', 'top-right', 'bottom-left', 'bottom-right'][index]
        return None

    def resize_rect(self, event):
        rect = self.rect()
        new_pos = event.pos()
        if self.resize_direction == 'top-left':
            # 确保不允许左上角反向拖动到右上角的右边
            if new_pos.x() > self.rect().topRight().x() - self.min_size:
                new_pos.setX(self.rect().topRight().x() - self.min_size)
            if new_pos.y() > self.rect().bottomRight().y() - self.min_size:
                new_pos.setY(self.rect().bottomRight().y() - self.min_size)
            # 左上角不允许超过父控件的左上角
            if new_pos.x() < 0:
                new_pos.setX(0)
            if new_pos.y() < 0:
                new_pos.setY(0)
            rect.setTopLeft(new_pos)
        elif self.resize_direction == 'top-right':
            # 确保不允许右上角反向拖动到左上角的左边
            if new_pos.x() < self.rect().topLeft().x() + self.min_size:
                new_pos.setX(self.rect().topLeft().x() + self.min_size)
            if new_pos.y() > self.rect().bottomLeft().y() - self.min_size:
                new_pos.setY(self.rect().bottomLeft().y() - self.min_size)
            # 右上角不允许超过父控件的右上角
            if new_pos.x() > self.max_resize_x:
                new_pos.setX(self.max_resize_x)
            if new_pos.y() < 0:
                new_pos.setY(0)
            rect.setTopRight(new_pos)
        elif self.resize_direction == 'bottom-left':
            # 确保不允许左下角反向拖动到右下角的右边
            if new_pos.x() > self.rect().topRight().x() - self.min_size:
                new_pos.setX(self.rect().topRight().x() - self.min_size)
            if new_pos.y() < self.rect().topRight().y() + self.min_size:
                new_pos.setY(self.rect().topRight().y() + self.min_size)
            # 左下角不允许超过父控件的左下角
            if new_pos.x() < 0:
                new_pos.setX(0)
            if new_pos.y() > self.scene().height():
                new_pos.setY(self.scene().height())
            rect.setBottomLeft(new_pos)
        elif self.resize_direction == 'bottom-right':
            # 确保不允许右下角反向拖动到左下角的左边
            if new_pos.x() < self.rect().topLeft().x() + self.min_size:
                new_pos.setX(self.rect().topLeft().x() + self.min_size)
            if new_pos.y() < self.rect().topLeft().y() + self.min_size:
                new_pos.setY(self.rect().topLeft().y() + self.min_size)
            # 右下角不允许超过父控件的右下角
            if new_pos.x() > self.max_resize_x:
                new_pos.setX(self.max_resize_x)
            if new_pos.y() > self.max_resize_y:
                new_pos.setY(self.max_resize_y)
            rect.setBottomRight(new_pos)
        self.setRect(rect)

    def is_mouse_over_resize_handle(self, event):
        handles = self.get_handles()
        for index, handle in enumerate(handles):
            if handle.contains(event.pos()):
                # index对应了左上角,右上角,左下角,右下角
                self.setCursor(Qt.SizeFDiagCursor if index == 0 or index == 3 else Qt.SizeBDiagCursor)
                return True
        return False

    def get_handles(self):
        handles = [
            # 左上角
            QRectF(self.rect().topLeft() - QPointF(self.resize_handle_size / 2, self.resize_handle_size / 2),
                   QSizeF(self.resize_handle_size, self.resize_handle_size)),  # 手柄以左上角为中心
            # 右上角
            QRectF(self.rect().topRight() - QPointF(self.resize_handle_size / 2, self.resize_handle_size / 2),
                   QSizeF(self.resize_handle_size, self.resize_handle_size)),  # 手柄以右上角为中心
            # 左下角
            QRectF(self.rect().bottomLeft() - QPointF(self.resize_handle_size / 2, self.resize_handle_size / 2),
                   QSizeF(self.resize_handle_size, self.resize_handle_size)),  # 手柄以左下角为中心
            # 右下角
            QRectF(self.rect().bottomRight() - QPointF(self.resize_handle_size / 2, self.resize_handle_size / 2),
                   QSizeF(self.resize_handle_size, self.resize_handle_size))  # 手柄以右下角为中心
        ]
        return handles

    def update_label_position(self):
        """
        更新文本标签的位置
        :return:
        """
        # 获取矩形的左上角
        rect_center = self.rect().topLeft()
        # 将文本放在矩形左上角
        self.text_item.setPos(rect_center.x(),
                              rect_center.y())

    def update_label_text(self):
        """
        更新文本标签的内容
        :return:  None
        """
        content = f'{math.floor(self.rect().width() / self.w_scale)}x{math.floor(self.rect().height() / self.h_scale)} {self.classes_name}'
        self.text_item.setPlainText(content)

    def show_small_pic(self):
        """
        显示小图
        :return:
        """
        rect = self.rect()
        # 取左上角的坐标
        top_left = self.mapToScene(rect.topLeft())  # 获取左上角在场景中的坐标
        x1 = top_left.x() / self.w_scale
        y1 = top_left.y() / self.h_scale
        x2 = x1 + rect.width() / self.w_scale
        y2 = y1 + rect.height() / self.h_scale
        self.mouse_move_event_hook(x1, y1, x2, y2)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    view = QGraphicsView()
    scene = QGraphicsScene()
    view.setScene(scene)
    # 设置场景大小
    scene.setSceneRect(0, 0, 400, 300)
    # 添加可调整大小的矩形
    rect_item = ResizableRect(QRectF(50, 50, 100, 100), 400, 300)
    scene.addItem(rect_item)
    view.setRenderHint(QtGui.QPainter.Antialiasing)
    view.setFixedSize(400, 300)
    view.show()
    sys.exit(app.exec_())
